﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using USMCdotNET;

namespace USMC.Net
{
    public enum SelectedElement
    {
        None,
        Line,
        Pause,
        Loop,
        RectangleArea
    }
    /// <summary>
    /// Interaction logic for RoutCanvas.xaml
    /// </summary>
    public partial class RouteCanvas : UserControl
    {
        //объект устройства
        public Devices dev;
        //значение зума
        double zoomValue = 1.0;
        //старое значение зума (требуется для проверки разрешения зума)
        double oldZoomValue=1.0;
        //объект трансформации масштаба канвы
        ScaleTransform canvasScaleTransform;
        //максимальное значение шагов
        int maxSteps;
        //расстояние (в шагах) между линиями линейки
        int rulerStepInPoints=15;
        //шрифт значений линейки
        Typeface typeFace;
        //кисть отрисовки сетки
        Brush brush = Brushes.Black;
        //pучка для отрисовки сетки
        Pen gridPen;
        //ручка для отрисовки прямоугольноков линеек
        Pen rectPen = new Pen(Brushes.LightGray, 1);
        //ручка для отрисовки делений линейки
        Pen rulerPen = new Pen(Brushes.Black, 1);
        //расстояние (в шагах) между линиями сетки
        int gridStep=-1;
        //ссылка на первую точку маршрута
        PointControl firstPoint;
        //конвертеры позиций на канве в координаты устройства и обратно
        internal Func<double, double> posXToSteps;
        internal Func<double, double> posYToSteps;
        internal Func<double, double> stepsXToPos;
        internal Func<double, double> stepsYToPos;
        //точка прикрепления прокрутки левой кнопкой мыши
        Point deltaScrolling;
        //список всех точек
        IList<PointControl> points;
        //список выбранных точек
        IList<PointControl> selectedPoints;
        //массив, показывающий, у каких точек, вошедших в область выделения, надо сменить выделение, а у каких - нет
        bool[] selectionChecks;
        //выбранный элемент для добавления
        SelectedElement selectedMode;
        //буферный элемент (висящий на курсоре во время добавления)
        PointControl bufElement;
        //список векторов, определяющих смещение текущего положения точки от startDragPoint при перемещении точек
        IList<Point> dragPoints;
        //границы перетаскивания точек
        double[] dragBorders;
        //точка -начало вектора перетаскивания
        Point startDragPoint;
        //прямоугольник выделения
        Rectangle selectionRectangle;
        //параметр, указывающий, начато движение по маршруту, или нет
        bool movingStarted = false;
        //показывает, начато ли выделение рамкой
  //      bool selectionMove = false;
        //левая верхняя точка рамки выделения
        Point firstSelPoint;
        //правая нижняя точка рамки выделения
        Point secondSelPoint;
        //событие, возникающее, когда было изменено выделение точек
        event Action<object> selectionchanged;
        //стартовые параметры устройства по умолчанию
        public PointParameters standardParameters;
        //счетчик изменения зума
        double deltaZoom = 0;
        //точка, в которую прокручивать при апдейте скролла
        Point scrollCenter;
        //Время последнего зума колесом
        int lastZoomed;
        //блок с информацией о координатах точки
        TextBlock infoBlock;
        //точка, на которую наведен курсор
        PointControl focusPoint;
        //последняя точка
        PointControl lastPoint;
        //точка, для которой открывается меню
        PointControl currentPoint;
        //стандартные параметры прямоугольника
        public RectangleParameters rectangleParameters;
        //элемент управления, показывающий текущее положение на канве
        internal Image currentPosImage;
        public RouteCanvas()
        {
            InitializeComponent();
            //инициализация устройства
            dev = new Devices();
            dev.GetMODE();
            dev.MODE[0].Power = true;
            dev.MODE[1].Power = true;
            dev.SetMODE();
            //инициализация шрифта линеек
            typeFace=new Typeface(FontFamily,FontStyle,FontWeight,FontStretch);

            canvasScaleTransform = new ScaleTransform(zoomValue, zoomValue);
            mainCanvas.LayoutTransform = canvasScaleTransform;

            gridPen = new Pen(brush, 1);
            MaxSteps = 161100;
            
            standardParameters = new PointParameters() { st1 = dev.STPRMS[0], st2 = dev.STPRMS[1]};
            standardParameters.StepDivisor = 8;
            standardParameters.Speed = 1000;
            standardParameters.SlowStart1 = true;
            standardParameters.SlowStart2 = true;
            standardParameters.AutoBacklash1 = false;
            standardParameters.AutoBacklash2 = false;

            firstPoint = new PointControl(posXToSteps, posYToSteps, stepsXToPos, stepsYToPos, standardParameters);
            pointsCanvas.Children.Add(firstPoint);
            firstPoint.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(point_MouseLeftButtonDown);
            firstPoint.MouseLeftButtonUp += new MouseButtonEventHandler(point_MouseLeftButtonUpUp);
            firstPoint.PreviousShape = new Line();
            firstPoint.MouseRightButtonDown += new MouseButtonEventHandler(point_MouseRightButtonDown);
            firstPoint.MouseWheel += new MouseWheelEventHandler(point_MouseWheel);
            firstPoint.Color = Colors.Black;

            points = new List<PointControl>();
            selectedPoints = new List<PointControl>();
            points.Add(firstPoint);

            dragPoints = new List<Point>();
            dragBorders = new double[4];
            ResetDragBorders();
            //рамка выделения
            selectionRectangle = new Rectangle();
            selectionRectangle.Fill = Brushes.Transparent;
            selectionRectangle.Stroke = Brushes.Black;
            selectionRectangle.StrokeThickness = 1;
            selectionRectangle.MouseUp +=(object sender, MouseButtonEventArgs e)=>
                {
                    pointsCanvas.Children.Remove(selectionRectangle);
                    selectionRectangle.ReleaseMouseCapture();
                    selectionChecks = null;
                    selectionchanged(this);
                };
            selectionRectangle.MouseMove += (object sender,MouseEventArgs e)=>
                {
                    Point newPos=e.GetPosition(mainCanvas);
                    secondSelPoint = newPos;
                    SetSelRectangle();
                    double xmin = Canvas.GetLeft(selectionRectangle);
                    double xmax = xmin + selectionRectangle.Width;
                    double ymin = Canvas.GetTop(selectionRectangle);
                    double ymax = ymin + selectionRectangle.Height;
                    int k = 0;
                    while (k < points.Count)
                    {
                        PointControl p = points[k];
                        if (p.X > xmin && p.X < xmax && p.Y > ymin && p.Y < ymax)
                        {
                            if (!selectionChecks[k])
                            {
                                ChangeSelection(p);
                                selectionChecks[k] = true;
                            }
                        }
                        else
                            if (selectionChecks[k])
                            {
                                ChangeSelection(p);
                                selectionChecks[k] = false;
                            }
                        k++;
                    }
                };

            infoBlock = new TextBlock();
            pointsCanvas.Children.Add(infoBlock);
            lastPoint = firstPoint;

            rectangleParameters = new RectangleParameters
            {
                Shift=200,Dir=false
            };

            currentPosImage = new Image();
            currentPosImage.Source = new BitmapImage(new Uri("pack://application:,,/cancel.png"));
            currentPosImage.Width = 10;
            currentPosImage.Height = 10;
            this.pointsCanvas.Children.Add(currentPosImage);
            Canvas.SetZIndex(currentPosImage,100);
        }
        private void ChangeSelection(PointControl p)
        {
            if (p.IsSelected)
            {
                selectedPoints.Remove(p);
                p.IsSelected = false;
            }
            else
            {
                selectedPoints.Add(p);
                p.IsSelected = true;
            }
        }
        #region Properties
        /// <summary>
        /// Событие, зажигаемое при изменении выделения
        /// </summary>
        public event Action<object> SelectionChanged
        {
            add
            {
                selectionchanged += value;
            }
            remove
            {
                selectionchanged -= value;
            }
        }
        /// <summary>
        /// Массив выделенных точек
        /// </summary>
        public IList<PointControl> SelectedPoints
        {
            get
            {
                return selectedPoints;
            }
        }
        /// <summary>
        /// Ширина интервала между делениями линейки
        /// </summary>
        public int RulerStepInPoints
        {
            get
            {
                return rulerStepInPoints;
            }
            set
            {
                rulerStepInPoints = value;
                DrawRulers();
            }
        }
        /// <summary>
        /// Длина линейки в 1/8 шага
        /// </summary>
        public int MaxSteps
        {
            get { return maxSteps; }
            set
            {
                maxSteps = value;
                double stepsPerPoint = maxSteps / (pointsCanvas.Width*8);
                stepsXToPos = (x) => (x / (double)stepsPerPoint);
                stepsYToPos = (x) => (pointsCanvas.Height - x / (double)stepsPerPoint);
                posXToSteps =
                    (x) => ((x) * stepsPerPoint);
                posYToSteps = (x) => ((-x + pointsCanvas.Height) * stepsPerPoint);
                DrawRulers();
                CreateGrid();
            }
        }
        /// <summary>
        /// Ширина шага сетки
        /// </summary>
        public int GridStep
        {
            get { return gridStep; }
            set
            {
                gridStep = value;
                CreateGrid();
            }
        }
        /// <summary>
        /// стартовая точка
        /// </summary>
        public PointControl StartPoint
        {
            get { return firstPoint; }
        }
        /// <summary>
        /// Значение зума
        /// </summary>
        public double Zoom
        {
            get { return zoomValue; }
        }
        public double ZoomSLiderValue
        {
            set { zoomSlider.Value = value; }
            get
            {
                return zoomSlider.Value;
            }
        }
        #endregion
        #region DrawMethods
        /// <summary>
        /// Отрисовка сетки
        /// </summary>
        private void CreateGrid()
        {
                DrawingGroup group = new DrawingGroup();
                using (DrawingContext context = group.Open())
                {
                    context.DrawRoundedRectangle(Brushes.White, gridPen, new Rect(0, 0, mainCanvas.Width, mainCanvas.Height), 1, 1);
                    if (gridStep >0)
                    {
                        if (MaxSteps / gridStep <= 70 * Zoom)
                        {
                            double k = gridStep;
                            double size = maxSteps / k;
                            double interval = mainCanvas.Width / size;
                            for (int i = 0; i < size; i++)
                            {
                                double d = i * interval;
                                context.DrawLine(gridPen, new Point(0, d), new Point(mainCanvas.Width, d));
                                context.DrawLine(gridPen, new Point(d, 0), new Point(d, mainCanvas.Height));
                            }
                        }
                    }
                }
                backImage.Source = new DrawingImage(group);
        }
        /// <summary>
        /// Отрисовка линейки
        /// </summary>
        private void DrawRulers()
        {
            DrawingGroup group1 = new DrawingGroup();
            DrawingGroup group2 = new DrawingGroup();
            double stepsInPixel = maxSteps / verticalBar.Height;
            double stepsBetweenLines = stepsInPixel * rulerStepInPoints;
            if (stepsBetweenLines > 1000)
                stepsBetweenLines = stepsBetweenLines- stepsBetweenLines % 1000;
            else if (stepsBetweenLines > 100)
                stepsBetweenLines = stepsBetweenLines- stepsBetweenLines % 100;
            else if (stepsBetweenLines > 10)
                stepsBetweenLines = stepsBetweenLines - stepsBetweenLines % 10;
            else
                stepsBetweenLines = Math.Round(stepsBetweenLines, 0);
            double pointsBetweenLines = stepsBetweenLines / stepsInPixel;
            int maxCount = 10;
            if (maxCount == 0)
                maxCount = 1;
            using (DrawingContext context1 = group1.Open())
            using (DrawingContext context2 = group2.Open())
            {
                double c = pointsBetweenLines;
                int count = 1;
                double stepsCoord;
                context1.DrawRectangle(Brushes.LightGray, rectPen, new Rect(new Size(verticalBar.Width, verticalBar.Height)));
                context1.DrawLine(rulerPen, new Point(verticalBar.Width, c), new Point(verticalBar.Width / 1.5, c));
                context1.DrawText(new FormattedText((maxSteps).ToString(), System.Globalization.CultureInfo.CurrentCulture, FlowDirection.LeftToRight, typeFace, 10, Brushes.Black), new Point((verticalBar.Width / 1.5) - 20, 0));
                context2.DrawRectangle(Brushes.LightGray, rectPen, new Rect(new Size(horizontalBar.Height, horizontalBar.Width)));
                context2.DrawLine(rulerPen, new Point(0, c), new Point(horizontalBar.Height / 2.3, c));
               // context2.DrawText(new FormattedText(0.ToString(), System.Globalization.CultureInfo.CurrentCulture, FlowDirection.LeftToRight, typeFace, 10, Brushes.Black), new Point((horizontalBar.Height / 2.3),-3));
                while (c < verticalBar.Height)
                {
                    if (count % maxCount == 0)
                    {
                        stepsCoord = maxSteps - count * stepsBetweenLines;
                        context1.DrawLine(rulerPen, new Point(verticalBar.Width, c), new Point(verticalBar.Width / 1.5, c));
                        context1.DrawText(new FormattedText((stepsCoord).ToString(), System.Globalization.CultureInfo.CurrentCulture, FlowDirection.LeftToRight, typeFace, 10, Brushes.Black), new Point((verticalBar.Width / 1.5) - 20, c - 10));
                        context2.DrawLine(rulerPen, new Point(0, c), new Point(horizontalBar.Height / 2.3, c));
                        context2.DrawText(new FormattedText((maxSteps - stepsCoord).ToString(), System.Globalization.CultureInfo.CurrentCulture, FlowDirection.LeftToRight, typeFace, 10, Brushes.Black), new Point((horizontalBar.Height / 2.3), c - 10));

                    }
                    else
                    {
                        context1.DrawLine(rulerPen, new Point(verticalBar.Width, c), new Point(verticalBar.Width / 6, c));
                        context2.DrawLine(rulerPen, new Point(0, c), new Point(horizontalBar.Height / 1.25, c));
                    }
                    c += pointsBetweenLines;
                    count++;
                }
            }
            group2.Transform = new RotateTransform(-90);
            verticalBar.Source = new DrawingImage(group1);
            horizontalBar.Source = new DrawingImage(group2);
        }
        #endregion
        #region Zoom and Scroll
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            zoomSlider.Value += zoomSlider.SmallChange;
            UpdateScroll();
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            zoomSlider.Value -= zoomSlider.SmallChange;
            UpdateScroll();
        }
        private void Rectangle_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
            {
                    zoomSlider.Value += (double)e.Delta / 1000;
                    if (e.Timestamp - lastZoomed > 1000)
                    {
                        scrollCenter = e.GetPosition(mainCanvas);
                    }
                    lastZoomed = e.Timestamp;
                    UpdateScroll(scrollCenter);
                }
        }

        private void zoomSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (zoomSlider.Value > 0)
                zoomValue = zoomSlider.Value + 1;
            else
                zoomValue = 1 / (-zoomSlider.Value + 1);
            if ((oldZoomValue <= zoomValue) || (scroller.ComputedHorizontalScrollBarVisibility == Visibility.Visible && scroller.ComputedVerticalScrollBarVisibility == Visibility.Visible))
            {
                zoomValue = Math.Round(zoomValue, 2);
                if (zoomValue > 11)
                    zoomValue = 11;
                zoomLabel.Text = zoomValue.ToString();
                canvasScaleTransform.ScaleX = zoomValue;
                canvasScaleTransform.ScaleY = zoomValue;
                horizontalBar.Width = mainCanvas.Width * zoomValue;
                verticalBar.Height = zoomValue * mainCanvas.Height;
                double a = zoomSlider.Value;
                double limit = Math.Abs(a - deltaZoom);
                if (limit > 0.3)
                {
                    DrawRulers();
                }
                if (limit > 2)
                {
                    gridPen.Thickness = 1 / zoomValue;
                    CreateGrid();
                    deltaZoom = a;
                }
                double z=1/zoomValue;
                foreach (PointControl q in points)
                {
                    double bufX = q.X;
                    double bufY = q.Y;
                    q.LayoutTransform = new ScaleTransform(z, z);
                    q.X = bufX;
                    q.Y = bufY;
                }
                currentPosImage.LayoutTransform = new ScaleTransform(z, z);
                infoBlock.LayoutTransform = new ScaleTransform(1 / zoomValue, 1 / zoomValue);
                oldZoomValue = zoomValue;
            }
            else
            {
                zoomValue = oldZoomValue;
                if (zoomValue > 1)
                    zoomSlider.Value = zoomValue - 1;
                else
                    zoomSlider.Value = 1 - 1 / zoomValue;
            }
        }
        //ввод зума с текстбокса
        private void zoomLabel_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                double t;
                if (!Double.TryParse(zoomLabel.Text, out t))
                    t = zoomValue;
                else if (t < 0.17 || t > 6)
                    t = zoomValue;
                if (t > 1)
                    zoomSlider.Value = t - 1;
                else
                    zoomSlider.Value = 1 - 1 / t;
                zoomLabel.Text = t.ToString();
            }
        }
        //метод, обновляющий скролл после зума (скролл в указанную точку)
        private void UpdateScroll(Point pos)
        {
            //scroller.UpdateLayout();
            //double prY, prX;
            //if (scroller.ScrollableHeight != 0)
            //{
            //    prY = scroller.ScrollableHeight/mainCanvas.Height;
            //}
            //else
            //    prY = 0.5;
            //if (scroller.ScrollableWidth != 0)
            //{
            //    prX = scroller.ScrollableWidth/mainCanvas.Width;
            //}
            //else
            //    prX = 0.5;
            //scroller.ScrollToVerticalOffset(prY * pos.Y);
            //scroller.ScrollToHorizontalOffset(prX * pos.Y);
            scroller.ScrollToLeftEnd();
            scroller.ScrollToEnd();
        }
        //метод, обновляющий скролл после зума(с сохранением пропорций скролла после зума)
        private void UpdateScroll()
        {
            //double prY, prX;
            //if (scroller.ScrollableHeight != 0)
            //    prY = scroller.VerticalOffset / scroller.ScrollableHeight;
            //else
            //    prY = 0.5;
            //if (scroller.ScrollableWidth != 0)
            //    prX = scroller.HorizontalOffset / scroller.ScrollableWidth;
            //else
            //    prX = 0.5;
            //scroller.UpdateLayout();
            //scroller.ScrollToVerticalOffset(prY * scroller.ScrollableHeight);
            //scroller.ScrollToHorizontalOffset(prX * scroller.ScrollableWidth);
            scroller.ScrollToLeftEnd();
            scroller.ScrollToEnd();
        }
        private void scroller_ScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            verticalBar.Margin = new Thickness(0, verticalBar.Margin.Top - e.VerticalChange, 0, 0);
            horizontalBar.Margin = new Thickness(horizontalBar.Margin.Left - e.HorizontalChange, 0, 0, 0);
        }

        #endregion
        //смещение при прокрутке мышью
        private void mainCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                Point newPos = e.GetPosition(mainCanvas);
               
                if ((Keyboard.Modifiers & ModifierKeys.Shift) != ModifierKeys.Shift)
                {
                    scroller.ScrollToVerticalOffset(scroller.ContentVerticalOffset+(deltaScrolling.Y - newPos.Y)*(zoomValue-1));
                    scroller.ScrollToHorizontalOffset(scroller.ContentHorizontalOffset + (deltaScrolling.X - newPos.X)*(zoomValue-1));
                    deltaScrolling = Mouse.GetPosition(mainCanvas);
                }
            }
        }
        //установка рамки выбора по двум точкам (firstPoint и secondPoint)
        private void SetSelRectangle()
        {
            double x;
            double y;
            if (firstSelPoint.X < secondSelPoint.X)
            {
                x = firstSelPoint.X;
                selectionRectangle.Width = (secondSelPoint.X - x);
            }
            else
            {
                x = secondSelPoint.X;
                selectionRectangle.Width = (firstSelPoint.X - x) ;
            }
            if (firstSelPoint.Y < secondSelPoint.Y)
            {
                y = firstSelPoint.Y;
                selectionRectangle.Height = (secondSelPoint.Y - y) ;
            }
            else
            {
                y = secondSelPoint.Y;
                selectionRectangle.Height = (firstSelPoint.Y - y);
            }
            Canvas.SetLeft(selectionRectangle, x);
            Canvas.SetTop(selectionRectangle, y);
            double width = selectionRectangle.Width;
            double htight = selectionRectangle.Height;
            
        }
        #region pointMouse
        public void point_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            PointControl s = sender as PointControl;
            focusPoint = s;
            UpdateInfoBoxCoords();
            infoBlock.Visibility = Visibility.Visible;
            s.CaptureMouse();
            if (!s.IsSelected)
            {
                if ((Keyboard.Modifiers & ModifierKeys.Shift) != ModifierKeys.Shift)
                    DeselectAll();
                s.IsSelected = true;
                selectedPoints.Add(s);
                dragPoints.Clear();
            }
            
            //startDragPoint = e.GetPosition(pointsCanvas);
            startDragPoint = new Point(s.X, s.Y);
            ResetDragBorders();
            foreach (PointControl p in selectedPoints)
            {
                Point point = new Point(startDragPoint.X - p.X, startDragPoint.Y - p.Y);
                dragPoints.Add(point);
                double[] buf = new double[4] { p.X, pointsCanvas.Width - p.X, p.Y, pointsCanvas.Height - p.Y };
                for (int i = 0; i < 4; i++)
                    if (buf[i] < dragBorders[i])
                        dragBorders[i] = buf[i];
            }
            movingStarted = true;
        }
        public void point_MouseRightButtonDown(object sender, MouseEventArgs e)
        {
            PointControl p = sender as PointControl;
            if(p!=null)
            {
                if(p.IsSelected)
                {
                  //  Canvas.SetLeft(contextMenu1, p.X);
                  //  Canvas.SetTop(contextMenu1, p.Y);
                    contextMenu1.Visibility = Visibility.Visible;
                    if (p.NextInStack != p)
                        cycleMenuItem.Visibility = Visibility.Visible;
                    else
                        cycleMenuItem.Visibility = Visibility.Collapsed;
                    e.Handled = true;
                    currentPoint = p;
                }
            }
        }
        void point_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            PointControl p = sender as PointControl;
            if (p.IsSelected)
            {
                if (e.Delta>100)
                {
                    if (Keyboard.IsKeyDown(Key.LeftShift))
                        DeselectAll();
                    else
                    {
                        p.IsSelected = false;
                        selectedPoints.Remove(p);
                    }
                    Canvas.SetZIndex(p, 0);
                    Canvas.SetZIndex(p.NextInStack, 5);
                    if (!p.NextInStack.IsSelected)
                    {
                        selectedPoints.Add(p.NextInStack);
                        p.NextInStack.IsSelected = true;
                    }
                    pointsCanvas.UpdateLayout();
                    mainCanvas.UpdateLayout();
                }
                e.Handled = true;
            }
        }
        //обновление координат блока с координатами
        private void UpdateInfoBoxCoords()
        {
            if (focusPoint != null)
            {
                Canvas.SetTop(infoBlock, focusPoint.Y);
                Canvas.SetLeft(infoBlock, focusPoint.X);
                infoBlock.Text = "X= " + focusPoint.XSteps + " Y= " + focusPoint.YSteps + " D= " + focusPoint.Parameters.StepDivisor;
            }
        }
        void point_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                if (movingStarted)
                {
                    Point newPos = e.GetPosition(pointsCanvas);
                    if(selectedPoints.Count==1)
                    {
                        PointControl b = selectedPoints[0];
                        if (b.NextInStack == b)
                        {
                            if (!Keyboard.IsKeyDown(Key.LeftCtrl))
                                SearchStickyPoints(ref newPos, b);
                        }
                        else
                        {
                            if (Math.Abs(newPos.X - b.X) >= 12 / zoomValue || Math.Abs(newPos.Y - b.Y) >= 12 / zoomValue)
                            {
                                Unstick(b);
                            }
                            return;
                        }
                    }
                    double deltaX = newPos.X - startDragPoint.X;
                    double deltaY = newPos.Y - startDragPoint.Y;
                    bool correctX = true;
                    bool correctY = true;
                    if (deltaX < 0)
                        correctX = (-deltaX < dragBorders[0]);
                    else
                        correctX = (deltaX < dragBorders[1]);
                    if (deltaY < 0)
                        correctY = (-deltaY < dragBorders[2]);
                    else
                        correctY = (deltaY < dragBorders[3]);
                    if (correctX || correctY)
                    {
                        Action<int> incr = null;
                        if (correctX)
                            incr += (int i) => { selectedPoints[i].X = newPos.X - dragPoints[i].X; };
                        if (correctY)
                            incr += (int i) => { selectedPoints[i].Y = newPos.Y - dragPoints[i].Y; };
                        for (int i = 0; i < selectedPoints.Count; i++)
                            incr(i);
                    }
                    UpdateInfoBoxCoords();
                }
            }
        }

        void point_MouseLeftButtonUpUp(object sender, MouseButtonEventArgs e)
        {
            PointControl s = sender as PointControl;
            s.ReleaseMouseCapture();
            /*if ((Keyboard.Modifiers & ModifierKeys.Shift) != ModifierKeys.Shift)
            {
                foreach (PointControl p in selectedPoints)
                    p.IsSelected = false;
                selectedPoints.Clear();
                dragPoints.Clear();
            }*/
            movingStarted = false;
            focusPoint = null;
            infoBlock.Visibility = Visibility.Collapsed;
            s.CorrectCoordinates(true);
            selectionchanged(this);
        }
        #endregion
        #region PlacingElements
        public SelectedElement PlaceableElement
        {
            get { return selectedMode; }
            set
            {
                if(selectedMode==SelectedElement.Line)
                    mainCanvas.MouseEnter -= new MouseEventHandler(mainCanvas_MouseEnter_CreatePointControl);
                else if (selectedMode == SelectedElement.RectangleArea)
                    mainCanvas.MouseEnter -= new MouseEventHandler(mainCanvas_MouseEnter_CreateRectArea);
                selectedMode = value;
                if (selectedMode == SelectedElement.Line)
                {
                    mainCanvas.MouseEnter += new MouseEventHandler(mainCanvas_MouseEnter_CreatePointControl);
                }
                else if(selectedMode==SelectedElement.None)
                {
                    RemoveCreatingPointControl();
                }
                else if (selectedMode == SelectedElement.RectangleArea)
                {
                    mainCanvas.MouseEnter += new MouseEventHandler(mainCanvas_MouseEnter_CreateRectArea);
                }
            }
        }
        #region RectangleArea
        //создание точки прямоугольной области как только мышь зайдет на канву
        void mainCanvas_MouseEnter_CreateRectArea(object sender, MouseEventArgs e)
        {
            AddCreatingRectangleAreaPointControl();
            mainCanvas.MouseEnter -= new MouseEventHandler(mainCanvas_MouseEnter_CreateRectArea);
        }
        //создание добавляемой прямоугольной обласи
        private void AddCreatingRectangleAreaPointControl()
        {
            Point p = Mouse.GetPosition(pointsCanvas);
            standardParameters.StepX = posXToSteps(p.X)*standardParameters.StepDivisor;
            standardParameters.StepY = posYToSteps(p.Y)*standardParameters.StepDivisor;
            bufElement = new PointControl(posXToSteps, posYToSteps, stepsXToPos, stepsYToPos, standardParameters);
            bufElement.PrevPoint = lastPoint;
            lastPoint.NextPoint = bufElement;
            AddRectangle(bufElement,rectangleParameters);
            bufElement.LayoutTransform = new ScaleTransform(1 / zoomValue, 1 / zoomValue);
            pointsCanvas.Children.Add(bufElement);
            bufElement.Opacity = 15;
            bufElement.CaptureMouse();
            bufElement.MouseMove += new MouseEventHandler(bufElement_MouseMove_With_Rectangle);
            bufElement.MouseLeftButtonDown += new MouseButtonEventHandler(bufElement_MouseLeftButtonDownWithRectArea);
            focusPoint = bufElement;
            UpdateInfoBoxCoords();
            infoBlock.Visibility = Visibility.Visible;
        }
        void bufElement_MouseLeftButtonDownWithRectArea(object sender, MouseEventArgs e)
        {
            Point p = e.GetPosition(pointsCanvas);
            if (bufElement.NextInStack == bufElement)
            {
                if (!Keyboard.IsKeyDown(Key.LeftCtrl))
                    SearchStickyPoints(ref p, bufElement);
            }
            else
            {
                p.X = bufElement.X;
                p.Y = bufElement.Y;
            }
            if (p.X >= 0 && p.X <= 500 && p.Y >= 0 && p.Y <= 500)
            {
                bufElement.X = p.X;
                bufElement.Y = p.Y;
                bufElement.CorrectCoordinates(false);
                bufElement.Opacity = 100.0;
                lastPoint = bufElement;
                bufElement.MouseMove -= new MouseEventHandler(bufElement_MouseMove_With_Rectangle);
                bufElement.MouseLeftButtonDown -= new MouseButtonEventHandler(bufElement_MouseLeftButtonDownWithRectArea);
                bufElement.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(point_MouseLeftButtonDown);
                bufElement.MouseLeftButtonUp += new MouseButtonEventHandler(point_MouseLeftButtonUpUp);
                bufElement.MouseRightButtonDown += new MouseButtonEventHandler(point_MouseRightButtonDown);
                bufElement.MouseWheel += new MouseWheelEventHandler(point_MouseWheel);
                points.Add(bufElement);
                bufElement.ReleaseMouseCapture();
                AddCreatingRectangleAreaPointControl();
            }
        }

        
        private void AddRectangle(PointControl p2, RectangleParameters param)
        {
            Rectangle rect = new Rectangle();
            rect.SetResourceReference(Rectangle.FillProperty, "areaBrush");
            rect.SetResourceReference(Rectangle.StrokeProperty, "strokeBrush");
            p2.PreviousShape = rect;
            p2.PrevPoint.NextShape = rect;
            p2.AssignShapeParameters(param);
            pointsCanvas.Children.Insert(0, rect);
        }
        private void bufElement_MouseMove_With_Rectangle(object sender, MouseEventArgs e)
        {
            deltaScrolling = e.GetPosition(pointsCanvas);
            if (bufElement.NextInStack == bufElement)
            {
                if (Keyboard.IsKeyDown(Key.LeftShift))
                    MakeSquareCoords(ref deltaScrolling);
                else if (!Keyboard.IsKeyDown(Key.LeftCtrl))
                    SearchStickyPoints(ref deltaScrolling, bufElement);
                if (deltaScrolling.X < 0)
                    bufElement.X = 0;
                else if (deltaScrolling.X > mainCanvas.Width)
                    bufElement.X = mainCanvas.Width;
                else
                    bufElement.X = deltaScrolling.X;
                if (deltaScrolling.Y < 0)
                    bufElement.Y = 0;
                else if (deltaScrolling.Y > mainCanvas.Height)
                    bufElement.Y = mainCanvas.Height;
                else
                    bufElement.Y = deltaScrolling.Y;
                UpdateInfoBoxCoords();
            }
            else if (Math.Abs(bufElement.X - deltaScrolling.X) >= 12 || Math.Abs(bufElement.Y - deltaScrolling.Y) >= 12)
                Unstick(bufElement);
        }
        //преобразует точку так, чтобы она составляла квадрат с последней точкой
        private void MakeSquareCoords(ref Point p)
        {
                double x = lastPoint.X;
                double y = lastPoint.Y;
                double w = p.X - x;
                double h = p.Y - y;
                double absw = Math.Abs(w);
                double absh = Math.Abs(h);
                if (absw > absh)
                {
                    if (h > 0)
                        p.Y = y + absw;
                    else
                        p.Y = y - absw;
                }
                else
                {
                    if (w > 0)
                        p.X = x + absh;
                    else
                        p.X = x - absh;
                }
        }
        #endregion
        #region Line
        //создание новой точки как только мышь зайдет на канву
        void mainCanvas_MouseEnter_CreatePointControl(object sender, MouseEventArgs e)
        {
            AddCreatingLinePointControl();
            mainCanvas.MouseEnter -= new MouseEventHandler(mainCanvas_MouseEnter_CreatePointControl);
        }
        //удаление добавляемой точки маршрута для линии
        private void RemoveCreatingPointControl()
        {
            if (bufElement != null)
            {
                bufElement.ReleaseMouseCapture();
                pointsCanvas.Children.Remove(bufElement);
                bufElement.MouseMove -= new MouseEventHandler(bufElement_MouseMove);
                bufElement.MouseLeftButtonDown -= new MouseButtonEventHandler(bufElement_MouseLeftButtonDownWithElement);
                pointsCanvas.Children.Remove(bufElement.PreviousShape);
                lastPoint = bufElement.PrevPoint;
                bufElement.PrevPoint.NextPoint = null;
                bufElement.PrevPoint.NextShape = null;
                bufElement.PrevPoint.UpdateNextShape = null;
                bufElement.PrevPoint = null;
                bufElement.PreviousShape = null;
                bufElement.UpdatePreviousShape = null;
                Unstick(bufElement);
                bufElement = null;
                focusPoint = null;
                infoBlock.Visibility = Visibility.Collapsed;
            }
        }
        //TODO: убрать сообщения; запилить хоткеи; исправить принятия параметров для элементов (для прямоугольника корректировку размеров!!!) допилить масштабирование и изменение шага линии
        //создание добавляемой точки маршрута для линии
        private void AddCreatingLinePointControl()
        {
            Point p = Mouse.GetPosition(pointsCanvas);
            standardParameters.StepX = posXToSteps(p.X)*standardParameters.StepDivisor;
            standardParameters.StepY = posYToSteps(p.Y)*standardParameters.StepDivisor;
            bufElement = new PointControl(posXToSteps, posYToSteps, stepsXToPos, stepsYToPos, standardParameters);
            bufElement.LayoutTransform = new ScaleTransform(1 / zoomValue, 1 / zoomValue);
            bufElement.PrevPoint = lastPoint;
            lastPoint.NextPoint = bufElement;
            AddLine(bufElement);
            pointsCanvas.Children.Add(bufElement);
            bufElement.Opacity = 15;
            bufElement.CaptureMouse();
            bufElement.MouseMove += new MouseEventHandler(bufElement_MouseMove);
            bufElement.MouseLeftButtonDown += new MouseButtonEventHandler(bufElement_MouseLeftButtonDownWithElement);
            focusPoint = bufElement;
            UpdateInfoBoxCoords();
            infoBlock.Visibility = Visibility.Visible;
        }
        void bufElement_MouseLeftButtonDownWithElement(object sender, MouseButtonEventArgs e)
        {
            Point p = e.GetPosition(pointsCanvas);
            if (bufElement.NextInStack == bufElement)
            {
                if (!Keyboard.IsKeyDown(Key.LeftCtrl))
                    SearchStickyPoints(ref p, bufElement);
            }
            else
            {
                p.X = bufElement.X;
                p.Y = bufElement.Y;
            }
            if (p.X >= 0 && p.X <= 500 && p.Y >= 0 && p.Y <= 500)
            {
                bufElement.X = p.X;
                bufElement.Y = p.Y;
                bufElement.Opacity = 100.0;
                lastPoint = bufElement;
                bufElement.MouseMove -= new MouseEventHandler(bufElement_MouseMove);
                bufElement.MouseLeftButtonDown -= new MouseButtonEventHandler(bufElement_MouseLeftButtonDownWithElement);
                bufElement.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(point_MouseLeftButtonDown);
                bufElement.MouseLeftButtonUp += new MouseButtonEventHandler(point_MouseLeftButtonUpUp);
                bufElement.MouseRightButtonDown += new MouseButtonEventHandler(point_MouseRightButtonDown);
                bufElement.MouseWheel += new MouseWheelEventHandler(point_MouseWheel);
                points.Add(bufElement);
                bufElement.ReleaseMouseCapture();
                AddCreatingLinePointControl();
            }
        }
        public void AddLine(PointControl p2)
        {
            Line line = new Line();
            line.SetResourceReference(Line.StrokeProperty, "strokeBrush");
            line.StrokeThickness = 1;
            pointsCanvas.Children.Insert(0, line);
            p2.PreviousShape = line;
            p2.PrevPoint.NextShape = line;
        }
        #endregion
        //поиск ближайших точек, и в случае успеха - прилипание к ним
        private void SearchStickyPoints(ref Point point, PointControl stickablePoint)
        {
            PointControl current = firstPoint;
            bool found = (Math.Abs(current.X - point.X) < 12/zoomValue && Math.Abs(current.Y - point.Y) < 12/zoomValue)&&(stickablePoint!=current);
            double x = firstPoint.X, y = firstPoint.Y;
            while (!found && current.NextPoint != null)
            {
                current = current.NextPoint;
                x = current.X;
                y = current.Y;
                found = (stickablePoint != current && Math.Abs(x - point.X) < 12 / zoomValue && Math.Abs(y - point.Y) < 12 / zoomValue);
            }
            if (found)
            {
                point.X = x;
                point.Y = y;
                stickablePoint.NextInStack = current.NextInStack;
                current.NextInStack = stickablePoint;
                Canvas.SetZIndex(current, 0);
                Canvas.SetZIndex(stickablePoint, 5);
            }
        }
        //отлепляет указанную точку от стека
        private void Unstick(PointControl unstickablePoint)
        {
            PointControl last = unstickablePoint.NextInStack;
            while (last.NextInStack != unstickablePoint)
                last = last.NextInStack;
            last.NextInStack = unstickablePoint.NextInStack;
            unstickablePoint.NextInStack = unstickablePoint;
            Canvas.SetZIndex(last, 5);
            Canvas.SetZIndex(unstickablePoint, 0);
            unstickablePoint.ClearCycles();
        }
        void bufElement_MouseMove(object sender, MouseEventArgs e)
        {
            deltaScrolling = e.GetPosition(pointsCanvas);
            if (bufElement.NextInStack == bufElement)
            {
                if (!Keyboard.IsKeyDown(Key.LeftCtrl))
                    SearchStickyPoints(ref deltaScrolling, bufElement);
                if (deltaScrolling.X < 0)
                    bufElement.X = 0;
                else if (deltaScrolling.X > mainCanvas.Width)
                    bufElement.X = mainCanvas.Width;
                else
                    bufElement.X = deltaScrolling.X;
                if (deltaScrolling.Y < 0)
                    bufElement.Y = 0;
                else if (deltaScrolling.Y > mainCanvas.Height)
                    bufElement.Y = mainCanvas.Height;
                else
                    bufElement.Y = deltaScrolling.Y;
                UpdateInfoBoxCoords();
            }
            else if (Math.Abs(bufElement.X - deltaScrolling.X) >= 12 / zoomValue || Math.Abs(bufElement.Y - deltaScrolling.Y) >= 12 / zoomValue)
                Unstick(bufElement);
        }
        #endregion
        #region Выделение и антивыделение, а еще с ними удаление
        private void mainCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        { 
            mainCanvas.MouseMove += new MouseEventHandler(mainCanvas_MouseMove_With_Frame);
            if ((Keyboard.Modifiers & ModifierKeys.Shift) != ModifierKeys.Shift)
            {
                DeselectAll();
                selectionchanged(this);
            }
        }
        //
        public void mainCanvas_MouseMove_With_Frame(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                deltaScrolling = e.GetPosition(mainCanvas);
                selectionChecks = new bool[points.Count];
                UpdateInfoBoxCoords();
              //  selectionMove = true;
                firstSelPoint = deltaScrolling;
                secondSelPoint = deltaScrolling;
                selectionRectangle.StrokeThickness = 1 / zoomValue;
                SetSelRectangle();
                try
                {
                    pointsCanvas.Children.Add(selectionRectangle);
                    selectionRectangle.CaptureMouse();
                }
                catch { }
            }
            mainCanvas.MouseMove -= new MouseEventHandler(mainCanvas_MouseMove_With_Frame);
        }

        private void pointsCanvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (!e.Handled)
            {
                PlaceableElement = SelectedElement.None;
                DeselectAll();
                
            }
        }
        //метод,отменяющий выделение всех элементов
        public void DeselectAll()
        {
            foreach (PointControl p in selectedPoints)
            {
                p.IsSelected = false;
            }
            selectedPoints.Clear();
            dragPoints.Clear();
            focusPoint = null;
            infoBlock.Visibility = Visibility.Collapsed;
            selectionchanged(this);
            contextMenu1.Visibility = Visibility.Collapsed;
        }



        private void ResetDragBorders()
        {
            double d = pointsCanvas.Width;
            for (int i = 0; i < 4; i++)
                dragBorders[i] = d;
        }
        public void RemoveSelected()
        {
            if (firstPoint.IsSelected)
            {
                firstPoint.IsSelected = false;
                selectedPoints.Remove(firstPoint);
            }
            /*if (loopStart.IsSelected && !loopEnd.IsSelected)
            {
                MessageBox.Show("Выбран только один элемент цикла. ");
                loopEnd.IsSelected = true;
                selectedPoints.Add(loopEnd);
            }
            else if (loopEnd.IsSelected && !loopStart.IsSelected)
            {
                loopStart.IsSelected = true;
                selectedPoints.Add(loopStart);
            }*/
            foreach (PointControl p in selectedPoints)
            {
                if (p == lastPoint)
                    lastPoint = p.PrevPoint;
                p.PrevPoint.NextPoint = p.NextPoint;
                pointsCanvas.Children.Remove(p.PrevPoint.NextShape);
                p.PrevPoint.NextShape = null;
                if (p.NextPoint != null)
                {
                    p.NextPoint.PrevPoint = p.PrevPoint;
                    p.PrevPoint.NextShape = p.NextPoint.PreviousShape;
                }
                p.PrevPoint = null;
                p.NextPoint = null;
                pointsCanvas.Children.Remove(p);
            }
            selectedPoints.Clear();
            selectionchanged(this);
            contextMenu1.Visibility = Visibility.Collapsed;
        }
        #endregion

        private void Button_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Delete)
                RemoveSelected();
        }

        internal void RemoveBufElement()
        {
            if (bufElement != null)
            {
                ReleaseMouseCapture();
                RemoveCreatingPointControl();
            }
        }

        private void removeMenuItem_Click(object sender, RoutedEventArgs e)
        {
            RemoveSelected();
        }
        public event EventHandler cyclesMenuItemSeelected;
        private void cycleMenuItem_Click(object sender, RoutedEventArgs e)
        {
            if (cyclesMenuItemSeelected != null)
                cyclesMenuItemSeelected(currentPoint,null);
         //   windowedList list = new windowedList();
         //   //PointControl i = currentPoint.NextInStack;
         //   //pointsCanvas.Children.Remove(currentPoint);
         //   //while(i!=currentPoint)
         //   //{
         //   //    pointsCanvas.Children.Remove(i);
         //   //    i = i.NextInStack;
         //   //}
         //   list.AssignPointsStack(currentPoint);
         //   mainGrid.Children.Add(list);
         //   Point position = Mouse.GetPosition(pointsCanvas);
         ////   Canvas.SetLeft(list, position.X);
         ////   Canvas.SetTop(list, position.Y);
         //   Grid.SetZIndex(list, 100);
         //   Grid.SetColumn(list, 2);
         //   Grid.SetRow(list, 1);
         //   list.HorizontalAlignment = HorizontalAlignment.Center;
         //   list.VerticalAlignment = VerticalAlignment.Center;
         //   MouseButtonEventHandler handler =null;
         //   handler= new MouseButtonEventHandler((object s, MouseButtonEventArgs ea) =>
         //       {
         //           list.Visibility = Visibility.Visible;
         //           mainGrid.Children.Remove(list);
         //           list.UnassignPointsStack();
         //           //pointsCanvas.Children.Add(currentPoint);
         //           //PointControl it = currentPoint.NextInStack;
         //           //while (it != currentPoint)
         //           //{
         //           //    pointsCanvas.Children.Add(it);
         //           //    it = it.NextInStack;
         //           //}
         //           list = null;
         //           pointsCanvas.MouseDown -= handler;
         //       });
         //   pointsCanvas.MouseDown += handler;
        }
        //чтобы не прокручивалось pageup pagedown
        private void scroller_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if(e.Key==Key.PageUp || e.Key==Key.PageDown)
                e.Handled = true;
        }

        internal void MakeRectangleGrid(PointControl pc)
        {
            PointControl prevPoint = pc.PrevPoint;
            PointControl newPoint = new PointControl(posXToSteps, posYToSteps, stepsXToPos, stepsYToPos, pc.Parameters);
            newPoint.X = prevPoint.X;
            newPoint.Y = prevPoint.Y;
            newPoint.NextPoint = pc.NextPoint;
            newPoint.PrevPoint = pc;
            if(pc.NextPoint!=null)
                pc.NextPoint.PrevPoint = newPoint;
            pc.NextPoint = newPoint;
            newPoint.NextShape = pc.NextShape;
            pc.NextShape = null;
            RectangleParameters rc = (RectangleParameters)pc.GetShapeParameters();
            rc.Dir = !rc.Dir;
            AddRectangle(newPoint,rc);
            points.Insert(points.IndexOf(pc), newPoint);
            pointsCanvas.Children.Add(newPoint);
            newPoint.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(point_MouseLeftButtonDown);
            newPoint.MouseLeftButtonUp += new MouseButtonEventHandler(point_MouseLeftButtonUpUp);
            newPoint.MouseRightButtonDown += new MouseButtonEventHandler(point_MouseRightButtonDown);
            newPoint.MouseWheel += new MouseWheelEventHandler(point_MouseWheel);

            newPoint.NextInStack = prevPoint.NextInStack;
            prevPoint.NextInStack = newPoint;
            Canvas.SetZIndex(prevPoint, 0);
            Canvas.SetZIndex(newPoint, 5);

            if (rc.Dir)
            {
                double deltaX = (newPoint.XSteps - pc.XSteps) % rc.Shift;
                pc.XSteps += deltaX;
            }
            else
            {
                double deltaY = (newPoint.Y - prevPoint.Y)%rc.Shift;
                pc.YSteps += deltaY;
            }
        }
    }
}
